package com.project.frba.bi.descriptor;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.project.frba.bi.BiUtil;
import com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl;
import com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl;

@SuppressWarnings("unchecked")
public class DatabaseDescriptor implements Descriptor{
	
	private String filePath;
	private Map<String, Join> joins = new LinkedHashMap<String, Join>();
	private Map<String, Dimension> dimensions = new LinkedHashMap<String, Dimension>();
	private Document xmlDescriptorDocument;
	private String descriptorName;
	private String facttable;
	private String metric;
	private String label;
	private Class metricType;
	private String privilege;
	private String formatterClassName = null;
	private Integer currencyType = null;
	
	public DatabaseDescriptor(Document xmlDocument){	
		this.xmlDescriptorDocument = xmlDocument;
	}
	
	public DatabaseDescriptor(String filePath){
		this.filePath = filePath;
		File file = new File(filePath);
		loadFile(file);
	}
	
	public DatabaseDescriptor(File file){
		this.filePath = file.getAbsolutePath();
		loadFile(file);
	}

	private void loadFile(File file) {
		DocumentBuilderFactory dFactory = new DocumentBuilderFactoryImpl();
		Document document = null;
		try {
			document = dFactory.newDocumentBuilder().parse(file);
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
		this.xmlDescriptorDocument = document;
	}

	public String getDescriptorName() {
		return descriptorName;
	}

	public void setDescriptorName(String descriptorName) {
		this.descriptorName = descriptorName;
	}

	public Collection<Dimension> getDimensions() {
		return this.dimensions.values();
	}

	public String getFacttable() {
		return facttable;
	}
	
	public void setFacttable(String facttable) {
		this.facttable = facttable;
	}
	
	public Collection<Property> getProperties() {
		List<Property> properties = new ArrayList<Property>();
		
		for (Dimension dimension : this.getDimensions()) {
			properties.addAll(dimension.getProperties());
		}
		return properties;
	}
	
	private Collection<Property> getDefaultProperties(String value){
		Collection<Property> properties = this.getProperties();
		List<Property> defaultProperties = new ArrayList<Property>();
		for (Property property : properties) {
			if (property.getDefaultValue() != null && property.getDefaultValue().equals(value)) {
				defaultProperties.add(property);
			}
		}
		return defaultProperties;
	}
	
	public Collection<Property> getRowDefaultProperties(){
		return this.getDefaultProperties("row");
	}
	
	public Collection<Property> getColumnDefaultProperties(){
		return this.getDefaultProperties("col");
	}
	
	public Collection<Join> getJoins(){
		return this.joins.values();
	}
	
	public String getMetric() {
		return metric;
	}

	public void setMetric(String metric) {
		this.metric = metric;
	}
	
	public Class getMetricType(){
		return metricType;
	}
	
	public void setMetricType(Class metricType){
		this.metricType = metricType;
	}

	public Document getDescriptorDocument() {
		return xmlDescriptorDocument;
	}

	public void setDescriptorDocument(Document xmlDescriptorDocument) {
		this.xmlDescriptorDocument = xmlDescriptorDocument;
	}
	
	public Node getNode(String xpath) throws XPathExpressionException{
		return (Node)XPathFactoryImpl.newInstance().newXPath().evaluate(xpath, this.getDescriptorDocument(), XPathConstants.NODE);
	}
	
	public void loadDimensions() throws XPathExpressionException {
		Node dimensionsNode= this.getNode("/bi/facttable/dimensions");
		NodeList dimensionsList = dimensionsNode.getChildNodes();
		for (int i = 0; i < dimensionsList.getLength(); i++) {
			Node dimensionNode = dimensionsList.item(i);
			if(dimensionNode.getNodeName().equals("dimension")){
				String dimensionName = dimensionNode.getAttributes().getNamedItem("name").getNodeValue();
				Dimension dimension = new Dimension(dimensionName, this);
				dimension.loadDimension();				
			}
		}
	}
	
	public void loadFacttable() throws XPathExpressionException {
		Node facttableNode = this.getNode("/bi/facttable");
		NamedNodeMap facttableAttributes = facttableNode.getAttributes();
		this.setFacttable(facttableAttributes.getNamedItem("table").getNodeValue());
		this.setMetric(facttableAttributes.getNamedItem("metric").getNodeValue());
		this.setMetricType(BiUtil.getMetricType(facttableAttributes.getNamedItem("type").getNodeValue()));
		
		if(facttableAttributes.getNamedItem("formatter")!=null){
			this.setFormatterClassName(facttableAttributes.getNamedItem("formatter").getNodeValue());
			this.currencyType = (facttableAttributes.getNamedItem("currency") != null) ? 
					new Integer(facttableAttributes.getNamedItem("currency").getNodeValue()) : null;
			
		}
		/*this.setFormatterClassName(facttableAttributes.getNamedItem("formatter").getNodeValue());
		this.currencyType = (facttableAttributes.getNamedItem("currency") != null) ? 
				new Integer(facttableAttributes.getNamedItem("currency").getNodeValue()) : null;*/
		
		Node biNode = this.getNode("/bi");
		this.setDescriptorName(biNode.getAttributes().getNamedItem("name").getNodeValue());
		this.label = biNode.getAttributes().getNamedItem("label").getNodeValue();
		this.privilege = biNode.getAttributes().getNamedItem("privilege").getNodeValue();
	}
	
	

	public void loadJoins() throws XPathExpressionException {
		Node joinsNode = this.getNode("/bi/facttable/joins");
		NodeList joinsList = joinsNode.getChildNodes();
		for (int i = 0; i < joinsList.getLength(); i++) {
			Node joinNode = joinsList.item(i);
			if(joinNode.getNodeName().equals("join")){
				String joinName = joinNode.getAttributes().getNamedItem("name").getNodeValue();
				Join join = new Join(joinName, this);
				join.loadJoin();
			}
		}
	}

	public String getJoinName(String table) {
		if(table.equals("facttable")){
			return "facttable";
		}
		
		Join joinTable = this.joins.get(table);
		return joinTable.getName();
	}
	
	
	public void load(){
		try {
			this.loadDimensions();
			this.loadFacttable();
			this.loadJoins();
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
	}

	public void addDimension(Dimension dimension) {
		this.dimensions.put(dimension.getName(), dimension);
	}

	public void addJoin(Join join) {
		this.joins.put(join.getName(), join);	
	}
	
	public String getLabel(){
		return this.label;
	}
	
	public Property getPropertyByName(String propertyName) {
		for (String dimensionName : this.dimensions.keySet()) {
			Dimension dimension = this.dimensions.get(dimensionName);
			if(dimension.hasProperty(propertyName)){
				return dimension.getProperty(propertyName);
			}	
		}
		return null;
	}

	public String getPrivilege() {
		return privilege;
	}

	public void setPrivilege(String privilege) {
		this.privilege = privilege;
	}
	
	public Boolean hasPermission(String userPrivilege) {
		if(userPrivilege.equals("admin")){
			return true;
		} else if(userPrivilege.equals("general") && (getPrivilege().equals("general") || getPrivilege().equals("restringido")) ) {
			return true;
		} else if(userPrivilege.equals("restringido") && getPrivilege().equals("restringido")) {
			return true;
		}
		
		return false;
	}
	
	public void setLabel(String label){
		this.label = label;
	}
	
	public void removeDimension(String dimensionName){
		this.dimensions.remove(dimensionName);
	}
	
	public void removeJoin(String joinName){
		this.joins.remove(joinName);
	}

	public String getFormatterClassName() {
		return formatterClassName;
	}

	public void setFormatterClassName(String formatterClassName) {
		this.formatterClassName = formatterClassName;
	}

	public Integer getCurrencyType() {
		return currencyType;
	}

	public void setCurrencyType(Integer currencyType) {
		this.currencyType = currencyType;
	}

	public String getFilePath() {
		return this.filePath;
	}
	
}
